package vn.lumi.smarthome.ui;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;

import vn.lumi.smarthome.R;
import vn.lumi.smarthome.io.model.Floor;
import vn.lumi.smarthome.network.AllDeviceStatus;
import vn.lumi.smarthome.network.Config;
import vn.lumi.smarthome.network.ControlNormalDevice;
import vn.lumi.smarthome.network.ControlRGB;
import vn.lumi.smarthome.network.CreateScene;
import vn.lumi.smarthome.network.CreateSchedule;
import vn.lumi.smarthome.network.HC_ACK;
import vn.lumi.smarthome.network.Login;
import vn.lumi.smarthome.network.LoginFeedback;
import vn.lumi.smarthome.network.Reconnect;
import vn.lumi.smarthome.provider.SmartHomeProvider;
import vn.lumi.smarthome.ulti.Preference;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo.DetailedState;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;

import com.actionbarsherlock.app.SherlockFragmentActivity;

public class LoginActivity extends SherlockFragmentActivity implements OnClickListener{

	// constants for commands to AsyncTask
	private final static int DO_NOTHING = 0;
	private final static int OPEN_SOCKET = 1;
	private final static int LOGIN = 2;

	// command header in message to HC
	private final static byte CMD_LOGIN = (byte) 0xA0;
	private final static byte CMD_LOGN_REPLY = (byte) 0xA2;

	private EditText edtUid;
	private EditText edtPwd;
	private CheckBox ckbAutologin;
	private Button btnLogin;
	private int Config_ver = 1;
	private int LastSessionID = 1;
	Socket socket = new Socket();
	String userName;
	String password;

	String LAN_ip = "192.168.1.109";
	int LAN_port = 8888;
	String DNS_address = "";
	String WAN_ip = "192.168.1.109";
	int WAN_port = 8888;
	boolean socket_working = false;
	InputStream is;
	OutputStream os;

	private static boolean  haveWifi = false;
	private static boolean  have3G = false;
	private static boolean haveLAN = false;
	private static boolean haveWAN = false;

	byte isUserValid;
	int Session_id = 0;
	int User_id = 0;
	int HC_Config_ver = 0;

	byte[] concat(byte[]... arrays) {
		// Determine the length of the result array
		int totalLength = 0;
		for (int i = 0; i < arrays.length; i++) {
			totalLength += arrays[i].length;
		}

		// create the result array
		byte[] result = new byte[totalLength];

		// copy the source arrays into the result array
		int currentIndex = 0;
		for (int i = 0; i < arrays.length; i++) {
			System.arraycopy(arrays[i], 0, result, currentIndex,
					arrays[i].length);
			currentIndex += arrays[i].length;
		}

		return result;
	}

	byte[] StringToByte(String str) {
		byte[] result = new byte[20];
		byte[] temp = null;
		try {
			temp = str.getBytes("UTF-16BE");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		for (int i = 0; i < 20; i++) {
			if (i < temp.length)
				result[i] = temp[i];
			else
				result[i] = 0;
		}
		return result;
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_login);

		// -----------------------------test----------------------------------

		byte[] header = new byte[4];
		header[0] = (byte) 0xA3;
		header[1] = (byte) 0x01;
		header[2] = 2;
		header[3] = 1;
		byte[] r1 = StringToByte(getString(R.string.txt_room_guess));
		byte[] r1n = new byte[] { 3, 1 };

		byte[] d1 = StringToByte(getString(R.string.txt_ceiling_light));
		byte[] d1n = new byte[] { 2, 1, 2 };

		byte[] d2 = StringToByte(getString(R.string.txt_rgb_light));
		byte[] d2n = new byte[] { 7, 1, 1, 1, 3 };

		byte[] d3 = StringToByte("Đèn Trần");
		byte[] d3n = new byte[] { 8, 1, 2 };

		byte[] r2 = StringToByte("Phòng Ăn");
		byte[] r2n = new byte[] { 3, 4 };

		byte[] d4 = StringToByte("Điều hòa");
		byte[] d4n = new byte[] { 5, 1, 5 };

		byte[] d5 = StringToByte("Nóng lạnh");
		byte[] d5n = new byte[] { 6, 1, 6 };

		byte[] d6 = StringToByte("Rèm ngủ");
		byte[] d6n = new byte[] { 3, 2 };

		byte[] test = concat(header, r1, r1n, d1, d1n, d2, d2n, d3, d3n, r2,
				r2n, d4, d4n, d5, d5n, d6, d6n);
		Config cf = new Config();
		boolean a = cf.isCorrectFormat(test);
		cf.parseMessage(test);

		Reconnect rcn = new Reconnect(10, 20, 200);
		byte[] rcnb = rcn.generateByteArray();

		// public CreateScene(int uid, int sid, int scene_id, int[]
		// DeviceIDList, int[] DeviceStateList, int[] rgbIDList,
		// ArrayList<int[]> rgbStateList) {
		int[] DeviceIDList = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		int[] DeviceStateList = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		int[] rgbID = new int[] { 10, 15, 20 };
		int[] rgb1 = new int[] { 1, 250, 100 };
		int[] rgb2 = new int[] { 50, 60, 70 };
		int[] rgb3 = new int[] { 80, 120, 150 };
		ArrayList<int[]> rgbState = new ArrayList<int[]>();
		rgbState.add(rgb1);
		rgbState.add(rgb2);
		rgbState.add(rgb3);

		CreateScene cs = new CreateScene(10, 100, 50, DeviceIDList,
				DeviceStateList, rgbID, rgbState);
		byte[] sc = cs.generateByteArray();

		// public ControlNormalDevice(int uid, int sid, int[] DeviceIDList,
		// int[] DeviceStateList) {
		ControlNormalDevice cnd = new ControlNormalDevice(10, 25, DeviceIDList,
				DeviceStateList);
		byte[] cndb = cnd.generateByteArray();

		ControlRGB ctrgb = new ControlRGB(10, 25, rgbID, rgbState);
		byte[] ctgrg = ctrgb.generateByteArray();

		// public CreateSchedule(int uid, int sid, int schedule_id, int dev_id,
		// boolean repeat, boolean[] days, int[] start_time, int[] dev_state) {
		boolean[] days = new boolean[] { true, true, true, false, false, false,
				true };
		int[] starttime = new int[] { 20, 30 };
		int[] devstate = new int[] { 5 };
		CreateSchedule csch = new CreateSchedule(10, 20, 30, 15, true, days,
				starttime, devstate);
		sc = csch.generateByteArray();
		// -----------------------------test----------------------------------
		
		initView();		
		setupActionbar();
		initData();
	}
	private void initView(){
		// initialize variables
		edtUid = (EditText) findViewById(R.id.activity_login_edt_username);
		edtPwd = (EditText) findViewById(R.id.activity_login_edt_pwd);
		ckbAutologin = (CheckBox) findViewById(R.id.activity_login_chb_remember);
		btnLogin = (Button) findViewById(R.id.activity_login_btn_login);

		// Check autologin --> if yes: load last time Uid and Pwd
		btnLogin.setOnClickListener(this);
		ckbAutologin.setOnClickListener(this);
	}
	private void initData(){
		if(Preference.isRemember(getApplicationContext())){
			edtPwd.setText(Preference.getPWD(getApplicationContext()));
			edtUid.setText(Preference.getUID(getApplicationContext()));
			ckbAutologin.setChecked(true);
		}else{
			ckbAutologin.setChecked(false);
		}
	}
	private void setupActionbar(){
		getSupportActionBar().setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_actionbar));
	}
	public void TryLogin() {
		// check Uid and Pwd valid
		if (edtUid.getText().toString().length() > 10
				|| edtUid.getText().toString().length() == 0
				|| edtPwd.getText().toString().length() > 10
				|| edtPwd.getText().toString().length() == 0) {
			AlertDialog.Builder ErrorDialog = new AlertDialog.Builder(
					LoginActivity.this);
			ErrorDialog.setTitle(R.string.glb_txt_error);
			ErrorDialog.setMessage("Tên hoặc mật khẩu không hợp lệ");
			ErrorDialog.setNeutralButton(R.string.glb_txt_return,
					new DialogInterface.OnClickListener() {

						@Override
						public void onClick(DialogInterface dialog, int which) {
							dialog.cancel();
						}
					});
			ErrorDialog.show();
			return;
		}

		// Ask user to remove old data

		// Check Wifi, 3G
		ConnectivityManager connMgr = (ConnectivityManager) this
				.getSystemService(Context.CONNECTIVITY_SERVICE);

		final android.net.NetworkInfo wifi = connMgr
				.getNetworkInfo(ConnectivityManager.TYPE_WIFI);

		final android.net.NetworkInfo mobile = connMgr
				.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);

		if (wifi != null && wifi.isAvailable()
				&& wifi.getDetailedState() == DetailedState.CONNECTED) {
			haveWifi = true;
		}

		if (mobile != null && mobile.isAvailable()
				&& mobile.getDetailedState() == DetailedState.CONNECTED) {
			have3G = true;
		}

		if (haveWifi == false & have3G == false) {
			AlertDialog.Builder ErrorDialog = new AlertDialog.Builder(
					LoginActivity.this);
			ErrorDialog.setTitle(R.string.glb_txt_error);
			ErrorDialog.setMessage("Không tìm thấy mạng Wifi hoặc 3G");
			ErrorDialog.setNeutralButton(R.string.glb_txt_return,
					new DialogInterface.OnClickListener() {

						@Override
						public void onClick(DialogInterface dialog, int which) {
							dialog.cancel();
						}
					});
			ErrorDialog.show();
			return;
		}

		// save user and pwd
		userName = edtUid.getText().toString();
		password = edtPwd.getText().toString();

		// Create Socket and check LAN/WAN
		socket_working = true;
		haveLAN = false;
		haveWAN = false;
		new GetDataSocket(this,LOGIN).execute();

		// AsyncTask to login

	}

	public static class GetDataSocket extends AsyncTask<Void, String, byte[]> {
		private LoginActivity mActivity;
		private int command = DO_NOTHING;
		private int openSocketRetry = 0;
		private int loginRetry = 0;
		LoginFeedback loginFeedback = new LoginFeedback();
		AllDeviceStatus deviceStatus = new AllDeviceStatus();
		ArrayList<Floor> floors = new ArrayList<Floor>();

		private boolean[] NetStatus = new boolean[2];

		public GetDataSocket(LoginActivity activity,int Cmd) {
			super();
			command = Cmd;
			WeakReference<LoginActivity>reference=new WeakReference<LoginActivity>(activity);
			mActivity=reference.get(); 
		}

		@Override
		protected byte[] doInBackground(Void... params) {
			if (command == LOGIN) {
				if (haveWifi == true) {
					// open socket to LAN IP
					haveLAN = mActivity.CheckHCLogin("LAN", loginFeedback, deviceStatus,floors);
				}

				// if haveLAN = false or mobile is using 3G --> check WAN
				if (haveLAN == false) {
					haveWAN = mActivity.CheckHCLogin("WAN", loginFeedback, deviceStatus,
							floors);
				}
				//So sanh version database, neu' khac' -> cap nhat config moi vao db
				SmartHomeProvider.insertDataFromHCToDB(mActivity, floors);
			}
			return null;
		}

		@Override
		protected void onPostExecute(byte[] result) {
			// super.onPostExecute(result);
			Log.d("onPostExecute", "run now");
			if (haveLAN == false && haveWAN == false) {
				AlertDialog.Builder ErrorDialog = new AlertDialog.Builder(
						mActivity);
				ErrorDialog.setTitle(R.string.glb_txt_error);
				ErrorDialog
						.setMessage(R.string.txt_error_cannot_connect_hc);
				ErrorDialog.setNeutralButton(R.string.glb_txt_return,
						new DialogInterface.OnClickListener() {

							@Override
							public void onClick(DialogInterface dialog,
									int which) {
								dialog.cancel();
							}
						});
				ErrorDialog.show();
			} else {
				if (loginFeedback.getUserValid() == LoginFeedback.WRONG_USER) {
					AlertDialog.Builder ErrorDialog = new AlertDialog.Builder(
							mActivity);
					ErrorDialog.setTitle(R.string.glb_txt_error);
					ErrorDialog.setMessage(R.string.txt_error_invalid_user_pwd);
					ErrorDialog.setNeutralButton(R.string.glb_txt_return,
							new DialogInterface.OnClickListener() {

								@Override
								public void onClick(DialogInterface dialog,
										int which) {
									dialog.cancel();
								}
							});
					ErrorDialog.show();
					return;
				} else if (loginFeedback.getUserValid() == LoginFeedback.HC_FULL) {
					AlertDialog.Builder ErrorDialog = new AlertDialog.Builder(
							mActivity);
					ErrorDialog.setTitle(R.string.txt_error_login_fail);
					ErrorDialog
							.setMessage(R.string.txt_error_login_fail_in_two_device);
					ErrorDialog.setNeutralButton(R.string.glb_txt_return,
							new DialogInterface.OnClickListener() {

								@Override
								public void onClick(DialogInterface dialog,
										int which) {
									dialog.cancel();
								}
							});
					ErrorDialog.show();
					return;
				} else if (loginFeedback.getUserValid() == LoginFeedback.LOGIN_SUCCESSFUL) {
					// save data in db
				}
			}
		}
	}

	public boolean CheckHCLogin(String path, LoginFeedback loginFeedback,
			AllDeviceStatus deviceStatus, ArrayList<Floor> floors) {
		int openSocketRetry = 0;
		int loginRetry = 0;
		Log.d("Socket", "Openning socket to " + path + " HC");
		boolean HCConnected = false;
		while (openSocketRetry < 2) {
			try {
				socket.close();
				socket = new Socket();
				socket.setReceiveBufferSize(1000);
				if (path == "LAN") {
					socket.connect(new InetSocketAddress(LAN_ip, LAN_port),
							1000); // timeout = 1s
					socket.setSoTimeout(1000);
				} else if (path == "WAN") {
					socket.connect(
							new InetSocketAddress(DNS_address, WAN_port), 3000);
					socket.setSoTimeout(3000);
				}
				is = socket.getInputStream();
				os = socket.getOutputStream();
				HCConnected = true;
				Log.d("Socket", "Socket opened");
				break;
			} catch (IOException e) {
				Log.d("Socket", "Failed to open socket to " + path + ", retry="
						+ (openSocketRetry + 1));
				e.printStackTrace();
				HCConnected = false;
				openSocketRetry++;
			}
		}

		if (HCConnected == true) {
			// send login message to HC
			Login login = new Login(LastSessionID, userName, password,
					Config_ver);
			byte[] dataout = login.generateByteArray();
			while (loginRetry < 2) {
				try {
					os.write(dataout);
					Log.d("Socket", "Sent: " + dataout.toString());

					byte[] buffer_temp = new byte[1000];
					int read = is.read(buffer_temp);
					byte[] buffer = new byte[read];
					for (int i = 0; i < read; i++) {
						buffer[i] = buffer_temp[i];
					}
					Log.d("Socket",
							"login Feedback: " + Arrays.toString(buffer));
					if (loginFeedback.isCorrectFormat(buffer) == true) {
						HCConnected = true;
						loginFeedback.parseMessage(buffer);
					} else
						HCConnected = false;
					break;
				} catch (IOException e) {
					Log.d("Socket", "Can not read from HC, retry="
							+ (loginRetry + 1));
					e.printStackTrace();
					HCConnected = false;
					loginRetry++;
				}
			}

			if (HCConnected == true) { // get config from HC
				// tach ban tin Login Feedback
				if (loginFeedback.getUserValid() == LoginFeedback.LOGIN_SUCCESSFUL) {
					if (Config_ver == loginFeedback.getHCConfigVer()) {
						// nhan ban tin device status
						floors = null;
						byte[] buffer_temp = new byte[500];
						int read = 0;
						try {
							read = is.read(buffer_temp);
						} catch (IOException e) {
							e.printStackTrace();
							// gui ban tin logout

						}
						byte[] buffer = new byte[read];
						for (int i = 0; i < read; i++) {
							buffer[i] = buffer_temp[i];
						}
						Log.d("Socket", "status: " + Arrays.toString(buffer));
						deviceStatus = new AllDeviceStatus();
						deviceStatus.parseMessage(buffer);
					} else {
						// nhan ban tin config
						deviceStatus = null;
						for (;;) {
							byte[] buffer_temp = new byte[1000];
							int read = 0;
							try {
								read = is.read(buffer_temp);
							} catch (IOException e) {
								e.printStackTrace();
								// gui ban tin logout
							}
							byte[] buffer = new byte[read];
							for (int i = 0; i < read; i++) {
								buffer[i] = buffer_temp[i];
							}
							Log.d("Socket",
									"config: " + Arrays.toString(buffer));
							HC_ACK ack = new HC_ACK();
							if (ack.isCorrectFormat(buffer) == true) // ban tin
																		// xac
																		// nhan
								break;
							Config cf = new Config();
//							cf.parseMessage(buffer);
							floors.add(cf.getFloors(buffer));
						}
					}
				}
			}
		}
		return HCConnected;
	}

	public void processInfo(byte[] deviceStatus, ArrayList<byte[]> roomConfig) {
		if (deviceStatus != null) {

		}

		if (roomConfig != null) {
			int numOfFloor = roomConfig.size();
		}
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.activity_login_btn_login:
//			TryLogin();
			Intent intent=new Intent(getApplicationContext(), MainActivity.class);
			startActivity(intent);
			finish();
			break;
		case R.id.activity_login_chb_remember:
			Preference.saveRemember(getApplicationContext(), ckbAutologin.isChecked());
			if(ckbAutologin.isChecked()){
				Preference.savepwd(getApplicationContext(), edtPwd.getText().toString());
				Preference.saveUID(getApplicationContext(), edtUid.getText().toString());
			}
			break;

		default:
			break;
		}
	}

}
